package com.guojiang.guoaicodemother.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.guojiang.guoaicodemother.ai.AiCodeGenTypeRoutingService;
import com.guojiang.guoaicodemother.common.DeleteRequest;
import com.guojiang.guoaicodemother.constant.AppConstant;
import com.guojiang.guoaicodemother.core.AiCodeGeneratorFacade;
import com.guojiang.guoaicodemother.core.builder.VueProjectBuilder;
import com.guojiang.guoaicodemother.core.handler.StreamHandlerExecutor;
import com.guojiang.guoaicodemother.exception.BusinessException;
import com.guojiang.guoaicodemother.exception.ErrorCode;
import com.guojiang.guoaicodemother.exception.ThrowUtils;
import com.guojiang.guoaicodemother.mapper.AppMapper;
import com.guojiang.guoaicodemother.model.dto.appDto.AppAddRequest;
import com.guojiang.guoaicodemother.model.dto.appDto.AppAdminUpdateRequest;
import com.guojiang.guoaicodemother.model.dto.appDto.AppQueryRequest;
import com.guojiang.guoaicodemother.model.dto.appDto.AppUpdateRequest;
import com.guojiang.guoaicodemother.model.entity.App;
import com.guojiang.guoaicodemother.model.entity.User;
import com.guojiang.guoaicodemother.model.enums.ChatHistoryMessageTypeEnum;
import com.guojiang.guoaicodemother.model.enums.CodeGenTypeEnum;
import com.guojiang.guoaicodemother.model.vo.appVo.AppVO;
import com.guojiang.guoaicodemother.model.vo.userVo.UserVO;
import com.guojiang.guoaicodemother.service.AppService;
import com.guojiang.guoaicodemother.service.ChatHistoryService;
import com.guojiang.guoaicodemother.service.ScreenshotService;
import com.guojiang.guoaicodemother.service.UserService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static com.guojiang.guoaicodemother.constant.AppConstant.GOOD_APP_PRIORITY;
import static com.guojiang.guoaicodemother.constant.AppConstant.USER_DEFAULT_PAGE_SIZE;

/**
 * 应用 服务实现层。
 */
@Service
@Slf4j
public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService {

    @Resource
    private UserService userService;

    @Resource
    private AiCodeGeneratorFacade aiCodeGeneratorFacade;

    @Resource
    private ChatHistoryService chatHistoryService;

    @Resource
    private StreamHandlerExecutor streamHandlerExecutor;

    @Resource
    private VueProjectBuilder vueProjectBuilder;

    @Resource
    private ScreenshotService screenshotService;
    
    @Resource
    private AiCodeGenTypeRoutingService aiCodeGenTypeRoutingService;

    /**
     * 创建应用
     *
     * @param appAddRequest
     * @param request
     * @return
     */
    @Override
    public long addApp(AppAddRequest appAddRequest, HttpServletRequest request) {
        String initPrompt = appAddRequest.getInitPrompt();
        if (StrUtil.isBlank(initPrompt)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数错误");
        }
        User loginUser = userService.getLoginUser(request);
        App app = new App();
        BeanUtil.copyProperties(appAddRequest, app); // 将请求参数复制到实体类中
        app.setUserId(loginUser.getId());
        // 应用名称默认前十二个字符
        app.setAppName(initPrompt.substring(0, Math.min(initPrompt.length(), 12)));
        // 调用 AI 代码生成类型智能路由服务，直接返回代码生成类型枚举
        CodeGenTypeEnum codeGenTypeEnum = aiCodeGenTypeRoutingService.routeCodeGenType(initPrompt);
        app.setCodeGenType(codeGenTypeEnum.getValue());
        app.setPriority(0); // 默认非精选
//        // 默认随机封面
//        app.setCover("https://picsum.photos/300/220");
        boolean result = save(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "系统繁忙，请稍候重试！");
        return app.getId();
    }

    /**
     * 更新自己的应用
     *
     * @param appUpdateRequest
     * @param request
     * @return
     */
    @Override
    public boolean updateMyApp(AppUpdateRequest appUpdateRequest, HttpServletRequest request) {
        if (appUpdateRequest.getId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数错误");
        }
        User loginUser = userService.getLoginUser(request);
        App oldApp = getById(appUpdateRequest.getId());
        if (oldApp == null || !oldApp.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "应用不存在或无权修改！");
        }
        App app = new App();
        BeanUtil.copyProperties(appUpdateRequest, app);
        app.setEditTime(LocalDateTime.now());
        boolean result = updateById(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "系统繁忙，请稍候重试！");
        return true;
    }

    /**
     * 删除自己的应用
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @Override
    public boolean deleteMyApp(DeleteRequest deleteRequest, HttpServletRequest request) {
        if (deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数错误");
        }
        User loginUser = userService.getLoginUser(request);
        App oldApp = getById(deleteRequest.getId());
        if (oldApp == null || !oldApp.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "应用不存在或无权删除！");
        }
        boolean result = removeById(deleteRequest.getId());
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "系统繁忙，请稍候重试！");
        return result;
    }

    /**
     * 删除应用时，删除应用对应的历史对话记录
     *
     * @param id
     * @return
     */
    @Override
    public boolean removeById(Serializable id) {
        // 重写 AppService 的 removeById 方法，加入删除应用对应的历史对话记录的逻辑
        if (id == null) {
            return false;
        }
        long appId = Long.parseLong(id.toString());
        if (appId <= 0) {
            return false;
        }
        try {
            // 添加删除历史对话记录的逻辑
            chatHistoryService.deleteChatMessageByAppId(appId);
        } catch (Exception e) {
            log.error("删除历史对话记录失败，应用ID：{}", appId, e);
        }
        // 删除应用
        return super.removeById(id);
    }

    /**
     * 获取应用视图
     *
     * @param app
     * @return
     */
    @Override
    public AppVO getAppVO(App app) {
        if (app == null) {
            return null;
        }
        AppVO appVO = new AppVO();
        BeanUtil.copyProperties(app, appVO);
        Long userId = app.getUserId();
        if (userId != null) {
            User user = userService.getById(userId);
            UserVO userVO = userService.getUserVO(user);
            appVO.setUserVO(userVO);
        }
        return appVO;
    }

    /**
     * 获取应用封装列表
     *
     * @param appList
     * @return
     */
    @Override
    public List<AppVO> getAppVOList(List<App> appList) {
        // 如果 appList 为空，则直接返回空列表
        if (CollUtil.isEmpty(appList)) return new ArrayList<>();
        // 将需要查询的 userId 提取到 Set 集合中去重
        Set<Long> userIdSet = appList.stream()
                .map(App::getUserId)
                .collect(Collectors.toSet());
        // 根据 userIdSet 查询出所有用户
        // 通过 Stream 流收集到 Map 中【key => userId，value => userVO】，方便后续获取用户信息
        Map<Long, UserVO> userVOMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.toMap(User::getId, userService::getUserVO));
        // 处理 appList 的流，将 app 转换为 appVO
        return appList.stream().map(app -> {
            AppVO appVO = getAppVO(app);
            // 根据处理好的 userVOMap 设置用户信息
            appVO.setUserVO(userVOMap.get(app.getUserId()));
            return appVO;
        }).collect(Collectors.toList());
    }

    /**
     * 构建查询请求
     *
     * @param appQueryRequest
     * @return
     */
    @Override
    public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
        Long id = appQueryRequest.getId();
        String appName = appQueryRequest.getAppName();
        String cover = appQueryRequest.getCover();
        String initPrompt = appQueryRequest.getInitPrompt();
        String codeGenType = appQueryRequest.getCodeGenType();
        String deployKey = appQueryRequest.getDeployKey();
        Integer priority = appQueryRequest.getPriority();
        Long userId = appQueryRequest.getUserId();
        String sortField = appQueryRequest.getSortField();
        String sortOrder = appQueryRequest.getSortOrder();
        return QueryWrapper.create()
                .eq("id", id)
                .like("appName", appName)
                .like("cover", cover)
                .like("initPrompt", initPrompt)
                .eq("codeGenType", codeGenType)
                .eq("deployKey", deployKey)
                .eq("priority", priority)
                .eq("userId", userId)
                .orderBy(sortField, "ascend".equals(sortOrder));
    }

    /**
     * 根据 id 获取自己的应用详情
     *
     * @param id
     * @param request
     * @return
     */
    @Override
    public AppVO getMyAppVOById(long id, HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        App app = getById(id);
        if (app == null || !app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "应用不存在或无权查看");
        }
        return getAppVO(app);
    }

    /**
     * 分页获取自己的应用列表
     *
     * @param appQueryRequest
     * @param request
     * @return
     */
    @Override
    public Page<AppVO> listMyAppVOByPage(AppQueryRequest appQueryRequest, HttpServletRequest request) {
        long pageNum = appQueryRequest.getPageNum();
        long pageSize = appQueryRequest.getPageSize();
        // 限制每页最多查询 20 个应用
        ThrowUtils.throwIf(pageSize > USER_DEFAULT_PAGE_SIZE, ErrorCode.PARAMS_ERROR
                , "每页最多查询 " + USER_DEFAULT_PAGE_SIZE + " 个应用");
        // 只查询当前用户创建的应用
        User loginUser = userService.getLoginUser(request);
        appQueryRequest.setUserId(loginUser.getId());
        QueryWrapper queryWrapper = getQueryWrapper(appQueryRequest);
        // 分页查询
        Page<App> appPage = page(Page.of(pageNum, pageSize), queryWrapper);
        // 将 appPage 封装为 appVOPage
        Page<AppVO> appVOPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = getAppVOList(appPage.getRecords());
        appVOPage.setRecords(appVOList);
        return appVOPage;
    }

    /**
     * 分页获取精选应用列表
     *
     * @param appQueryRequest
     * @return
     */
    @Override
    public Page<AppVO> listGoodAppVOByPage(AppQueryRequest appQueryRequest) {
        long pageNum = appQueryRequest.getPageNum();
        long pageSize = appQueryRequest.getPageSize();
        // 限制每页最多查询 20 个应用
        ThrowUtils.throwIf(pageSize > USER_DEFAULT_PAGE_SIZE, ErrorCode.PARAMS_ERROR
                , "每页最多查询 " + USER_DEFAULT_PAGE_SIZE + " 个应用");
        // 只查询精选应用，即 priority >= 99 的应用
        QueryWrapper queryWrapper = getQueryWrapper(appQueryRequest);
        queryWrapper.ge("priority", GOOD_APP_PRIORITY);
        // 优先按优先级排序，优先级数值大的靠前，再按用户指定字段排序
        // 第二个参数 false 表示降序，true 表示升序
        queryWrapper.orderBy("priority", false)
                .orderBy(appQueryRequest.getSortField(), "ascend".equals(appQueryRequest.getSortOrder()));
        // 分页查询
        Page<App> appPage = page(Page.of(pageNum, pageSize), queryWrapper);
        // 将 appPage 封装为 appVOPage
        Page<AppVO> appVOPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = getAppVOList(appPage.getRecords());
        appVOPage.setRecords(appVOList);
        return appVOPage;
    }

    /**
     * 管理员删除任意应用
     *
     * @param deleteRequest
     * @return
     */
    @Override
    public boolean deleteAppForAdmin(DeleteRequest deleteRequest) {
        if (deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "要删除的应用 ID 为空");
        }
        boolean removeResult = removeById(deleteRequest.getId());
        ThrowUtils.throwIf(!removeResult, ErrorCode.OPERATION_ERROR, "删除应用失败");
        return true;
    }

    /**
     * 管理员更新任意应用
     *
     * @param appAdminUpdateRequest
     * @return
     */
    @Override
    public boolean adminUpdateApp(AppAdminUpdateRequest appAdminUpdateRequest) {
        long id = appAdminUpdateRequest.getId();
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "要更新的应用 ID 错误");
        }
        // 判断是否存在
        App oldApp = getById(id);
        ThrowUtils.throwIf(ObjectUtil.isNull(oldApp), ErrorCode.NOT_FOUND_ERROR, "要更新的应用不存在");
        App app = new App();
        BeanUtil.copyProperties(appAdminUpdateRequest, app);
        // 设置管理员更新时间
        app.setUpdateTime(LocalDateTime.now());
        boolean updateResult = updateById(app);
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新应用失败");
        return true;
    }

    /**
     * 管理员分页获取应用列表
     *
     * @param appQueryRequest
     * @return
     */
    @Override
    public Page<AppVO> listAppVOByPageForAdmin(AppQueryRequest appQueryRequest) {
        long pageNum = appQueryRequest.getPageNum();
        long pageSize = appQueryRequest.getPageSize();
        QueryWrapper queryWrapper = getQueryWrapper(appQueryRequest);
        // 分页查询
        Page<App> appPage = page(Page.of(pageNum, pageSize), queryWrapper);
        // 数据封装
        Page<AppVO> appVOPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = getAppVOList(appPage.getRecords());
        appVOPage.setRecords(appVOList);
        return appVOPage;
    }

    /**
     * 管理员获取应用详情
     *
     * @param id
     * @return
     */
    @Override
    public AppVO getAppVOByIdForAdmin(long id) {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "要查询的应用 ID 错误");
        }
        App app = getById(id);
        if (app == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "要查询的应用不存在");
        }
        return getAppVO(app);
    }

    /**
     * 对话 AI 生成代码（最核心接口）
     *
     * @param appId
     * @param message
     * @param loginUser
     * @return
     */
    @Override
    public Flux<String> chatToGenCode(Long appId, String message, User loginUser) {
        // 1. 校验参数合法性
        ThrowUtils.throwIf(ObjectUtil.isNull(appId) || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 错误");
        ThrowUtils.throwIf(ObjectUtil.isNull(message), ErrorCode.PARAMS_ERROR, "对话消息不能为空");
        // 2. 查询应用信息
        App app = getById(appId);
        // 3. 校验应用不能为空；权限校验，仅本人可以和自己的应用对话
        ThrowUtils.throwIf(ObjectUtil.isNull(app), ErrorCode.NOT_FOUND_ERROR, "对话失败，应用不存在");
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限对话该应用");
        }
        // 4. 获取应用的代码生成类型
        String codeGenType = app.getCodeGenType();
        CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenType);
        ThrowUtils.throwIf(ObjectUtil.isNull(codeGenTypeEnum), ErrorCode.PARAMS_ERROR, "应用代码生成类型错误");
        // 5. 调用 AI 前，先保存用户消息到数据库中
        chatHistoryService.addChatMessage(appId, message, ChatHistoryMessageTypeEnum.USER.getValue(), loginUser.getId());
        // 6. 注入 AI 代码生成门面，调用代码生成器，对话AI，生成代码
        Flux<String> codeStreaming = aiCodeGeneratorFacade
                .generateAndSaveCodeStreaming(message, codeGenTypeEnum, appId);
        // 7. 收集 AI 响应的内容，并且在对话完成后保存到数据库中
        // 调用 streamHandlerExecutor，根据代码类型使用不同的处理器（JsonMessageStreamHandler 或 SimpleTextStreamHandler）
        return streamHandlerExecutor.doExecute(codeStreaming, chatHistoryService, appId, loginUser, codeGenTypeEnum);
    }

    /**
     * 应用部署
     *
     * @param appId     应用ID
     * @param loginUser 当前登录用户
     * @return 可访问的部署地址
     */
    @Override
    public String deployApp(Long appId, User loginUser) {
        // 1. 参数校验
        ThrowUtils.throwIf(ObjectUtil.isNull(loginUser), ErrorCode.NOT_LOGIN_ERROR, "请先登录");
        ThrowUtils.throwIf(ObjectUtil.isNull(appId) || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 错误");
        App app = getById(appId);
        ThrowUtils.throwIf(ObjectUtil.isNull(app), ErrorCode.PARAMS_ERROR, "要部署的应用不存在");
        // 2. 权限校验，仅本人可以部署自己的应用
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限部署该应用");
        }
        // 3. 检查是否已经部署
        String deployKey = app.getDeployKey();
        // 4. 生成六位随机 deployKey（字母＋数字）
        if (StrUtil.isBlank(deployKey)) {
            deployKey = RandomUtil.randomString(6);
        }
        // 5. 获取应用代码生成类型，获取原始路径
        String codeGenType = app.getCodeGenType();
        String sourceDirName = codeGenType + "_" + appId;
        // 完整访问路径
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
        // 6. 检查路径是否存在
        File sourceDir = new File(sourceDirPath);
        if (!sourceDir.exists() || !sourceDir.isDirectory()) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用代码路径不存在，请先生成应用");
        }
        // 7. Vue 项目特殊处理，执行构建
        CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenType);
        if (codeGenTypeEnum == CodeGenTypeEnum.VUE_PROJECT) {
            // 确保部署的时候是最新的，部署前再执行一次构建
            boolean buildProject = vueProjectBuilder.buildProject(sourceDirPath);
            ThrowUtils.throwIf(!buildProject, ErrorCode.SYSTEM_ERROR, "Vue 项目构建失败，请稍候重试");
            // 检查 dist 目录是否存在
            File disDir = new File(sourceDirPath, "dist");
            ThrowUtils.throwIf(!disDir.exists() || !disDir.isDirectory(), ErrorCode.SYSTEM_ERROR, "Vue 项目构建失败，请稍候重试");
            // 复制文件到部署目录
            sourceDir = disDir;
        }
        // 8. 复制文件到部署目录
        String deployDirPath = AppConstant.CODE_DEPLOY_ROOT_DIR + File.separator + deployKey;
        // 覆盖上一次部署的内容
        try {
            File deployDir = new File(deployDirPath);
            FileUtil.copyContent(sourceDir, deployDir, true);
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用部署失败，请稍候重试，错误信息：" + e.getMessage());
        }
        // 9. 更新数据库部署地址和部署时间
        App updateApp = new App();
        updateApp.setId(appId);
        updateApp.setDeployKey(deployKey);
        updateApp.setDeployedTime(LocalDateTime.now());
        boolean updateResult = updateById(updateApp);
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新应用部署信息，请稍候重试");
        // 10. 返回可访问的 URL 地址
        String appDeployUrl = String.format("%s/%s", AppConstant.CODE_DEPLOY_HOST, deployKey);
        // 11. 异步调用截图服务，生成截图并更新应用封面
        generateAppScreenshotAsync(appId, appDeployUrl);
        return appDeployUrl;
    }

    /**
     * 异步生成应用封面图
     *
     * @param appId
     * @param appDeployUrl
     */
    @Override
    public void generateAppScreenshotAsync(Long appId, String appDeployUrl) {
        // 启动虚拟线程
        Thread.startVirtualThread(() -> {
            // 调用截图服务
            String screenshotUrl = screenshotService.generateAndUploadScreenshot(appDeployUrl);
            ThrowUtils.throwIf(StrUtil.isBlank(screenshotUrl), ErrorCode.SYSTEM_ERROR, "封面生成失败，请稍候重试");
            // 更新数据库中的封面地址
            App app = new App();
            app.setId(appId);
            app.setCover(screenshotUrl);
            boolean updateResult = this.updateById(app);
            ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新应用封面失败，请稍候重试");
        });
    }
}
